#include <linux/init.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/minmax.h>

#define LOGTAG "misc "

static int dev_param = 1234;
static int dev_args[5] = {11, 22, 33, 44, 55};
static int dev_argc = 5;

/* module_param(name, type, perm);
 * name：要传递进去的参数的名称
 * type：类型
 * perm：参数读写的权限 S_IRUSR 400
 */
module_param(dev_param, int, S_IRUSR);

/* module_param_array(name, type, &nump, perm);
 * name:要传递进去的参数的名称
 * type：类型
 * nump：实际传入的个数
 * perm：参数读写的权限
 */
module_param_array(dev_args, int, &dev_argc, S_IRUSR);

int misc_open(struct inode* inode, struct file* file) {
    printk("misc_open !!!\n");
    return 0;
}

int misc_release(struct inode* inode, struct file* file) {
    printk("misc_release !!!\n");
    return 0;
}

loff_t misc_llseek(struct file *file, loff_t loff, int whence) {
    printk("misc_llseek: loff = 0x%llX, whence = %d\n", loff, whence);
    return loff;
}

ssize_t misc_read(struct file* file, char __user* user, size_t size, loff_t* loff) {
    int ret;
    const char kbuf[64] = "this is from mymisc kernal driver.";
    printk("misc_read: size = %lu, loff = %p, *loff = %lld\n", size, loff, loff ? *loff : -1);

    ret = copy_to_user(user, kbuf, sizeof(kbuf));
    if(ret) {
        pr_err("copy_to_user error: %d\n", ret);
        return -EIO;
    }
    return 0;
}

ssize_t misc_write(struct file* file, const char __user* user, size_t size, loff_t* loff) {
    int dlen, ret;
    char kbuf[64];
    pr_info("misc_write: size = %lu, loff = %p, *loff = %lld\n", size, loff, loff ? *loff : -1);

    dlen = min(size, sizeof(kbuf)-1);
    ret = copy_from_user(kbuf, user, dlen);
    if(ret) {
        pr_err("copy_from_user error: %d\n", ret);
        return -EIO;
    }
    kbuf[dlen] = '\0';
    pr_info("%s\n", kbuf);
    return dlen;
}

long misc_unlocked_ioctl(struct file* file, unsigned int request, unsigned long option) {
    printk("misc_unlocked_ioctl: request = %d, option = %ld\n", request, option);
    return 5678;
}

__poll_t misc_poll(struct file *file, struct poll_table_struct *pts) {
    printk("misc_poll: pts = 0x%p\n", pts);
    return 0;
}

struct file_operations misc_fops = {
    .owner = THIS_MODULE,
    .open = misc_open,
    .release = misc_release,
    .llseek = misc_llseek,
    .read = misc_read,
    .write = misc_write,
    .unlocked_ioctl = misc_unlocked_ioctl,
    .poll = misc_poll,
};

struct miscdevice misc_dev = {
    .minor = MISC_DYNAMIC_MINOR, 
    .name = "mymisc", 
    .fops = &misc_fops 
};

static int misc_init(void) {
    int ret, i;

    pr_info(LOGTAG "dev_param = %d\n", dev_param);
    for(i=0; i<dev_argc; i++) {
        pr_info(LOGTAG "dev_args[%d] = %d\n", i, dev_args[i]);
    }

    ret = misc_register(&misc_dev);
    if(ret < 0) {
        printk("misc_register failed !!!\n");
        return -1;
    }

    printk("misc_register succeed !!!\n");  // 在内核中无法使用c语言库，所以不用printf
    return 0;
}

static void misc_exit(void) {
    misc_deregister(&misc_dev);
    printk("misc exit !!!\n");
}

module_init(misc_init);
module_exit(misc_exit);

MODULE_LICENSE("GPL");  // 声明模块拥有开源许可
